/**************************************************************************\
*//*! \file nanotube_pcap_dump.cpp
** \author  Stephan Diestelhorst <stephand@amd.com>
**          Neil Turton <neilt@amd.com>
**  \brief  Nanotube pcap dumping.
**   \date  2020-08-26
*//*
\**************************************************************************/

/**************************************************************************
** Copyright (C) 2023, Advanced Micro Devices, Inc. All rights reserved.
** SPDX-License-Identifier: MIT
**************************************************************************/

#include "nanotube_pcap_dump.hpp"

#include "nanotube_packet.hpp"
#include "nanotube_pcap_read.hpp"

#include <assert.h>
#include <iostream>

nanotube_pcap_dump::nanotube_pcap_dump(const char *file, bool with_metadata)
{
  pcap_dumper_t *res = nullptr;
  pcap_t *pcap       = pcap_open_dead(with_metadata ? DLT_PPI : DLT_EN10MB, 0xffff);

  res = pcap_dump_open(pcap, file);

  if( res == nullptr ) {
    std::cerr << "pcap dump: Error opening packet container: "
              << pcap_geterr(pcap) << '\n';
  }
  dumper = res;
  m_with_metadata = with_metadata;
}

nanotube_pcap_dump::~nanotube_pcap_dump()
{
  if( dumper != nullptr )
    pcap_dump_close(dumper);
}

void nanotube_pcap_dump::write(nanotube_packet_t *p)
{
  if( dumper == nullptr )
    return;

  struct pcap_pkthdr hdr;
  hdr.caplen = p->size(NANOTUBE_SECTION_PAYLOAD);
  hdr.len    = hdr.caplen;
  hdr.ts.tv_sec  = 0;
  hdr.ts.tv_usec = 0;

  uint8_t* data = p->begin(NANOTUBE_SECTION_PAYLOAD);
  // If we aren't emitting metadata things are simpler.
  if (!m_with_metadata) {
    pcap_dump((u_char*)dumper, &hdr, data);
    return;
  }

  // Add PPI header. See comments in nanotube_pcap_read. We could be a bit
  // smarter here and save the 4-byte field header in case there is no
  // metadata.
  uint16_t md_len = p->size(NANOTUBE_SECTION_METADATA);
  uint16_t pph_len = 12 + md_len;
  // FIXME: we should have a proper metadata type identifier. Use 0xF00D
  // bodged-up value for now (as generated by scripts/p4_slice_test_to_nt).
  static_assert(DLT_EN10MB < 256);
  uint8_t ppi_headers[12] = { 0, 0, (uint8_t)(pph_len & 0xff), (uint8_t)(pph_len >> 8), DLT_EN10MB,0,0,0,
                              0x0d, 0xf0, (uint8_t)(md_len & 0xff), (uint8_t)(md_len >> 8) };
  uint8_t* buf = (uint8_t*)malloc(12 + hdr.caplen);
  memcpy(buf, ppi_headers, 12);
  memcpy(buf + 12, data, hdr.caplen);
  hdr.caplen += 12 + md_len;
  hdr.len += 12 + md_len;
  pcap_dump((u_char*)dumper, &hdr, buf);
  free(buf);
}
